# 面向对象 定义类 类创建对象
# 创建类的语法
# class A:   //python2.写法
# class A(object)   //python2.写法
"""class Student():   # 类名要符合大驼峰写法（首字母大写）
    # __init__(self)  魔法函数  (构造函数)，可以接收参数
    # self相当于this，指向调用此方法的对象
    def __init__(self, name, age):  # 接收创建对象时候传进来的参数
        self.school = '魔法学院'
        self.name = name
        self.age = age

    # __str__(self)  魔法函数  对象说明
    def __str__(self):   # 在print（类的对象）的时候接收到的内容，不写则接收内存地址
        return '这是' + self.school + '的学生，名字是' + self.name

    # __del__(self) 魔法函数 删除对象
    def __del__(self):   # 系统自动调用 删除对象
        print('对象已被删除')

    def info(self):
        print(self.school, self.name, self.age)

# 创建类的对象
student1 = Student('caicai', 18)
# 如果定义类的时候没有写 __str__魔法函数，则输出的是对象所在的内存地址
# 定义l__str__魔法函数则输出return的内容
print(student1)
student1.info()
student2 = Student('miaomiao', 17)
student2.info()
"""

# 类的应用 --- 烤地瓜
"""class Cookphotato():
    def __init__(self):
        self.time = 0    # 烤地瓜的时间
        self.static = '生的'   # 地瓜状态
        self.cooklist = []    # 添加的佐料

    def cook_time(self, time):
        self.time += time
        if 0 < self.time < 3:
            self.static = '生的'
        elif 3 <= self.time < 5:
            self.static = '半生的'
        elif 5 <= self.time < 8:
            self.static = '熟了'
        elif self.time >=  8:
            self.static = '烤糊了'

    def add_list(self,conmenu):  # 添加佐料
        self.cooklist.append(conmenu)

    def __str__(self):
        return f'地瓜烤了{self.time}分钟，结果是{self.static}，添加的佐料有{self.cooklist}'

cook1 = Cookphotato()
cook1.cook_time(5)
print(cook1)

cook2 = Cookphotato()
cook2.cook_time(15)
cook2.add_list('辣椒面')
cook2.add_list('酱油')
print(cook2)
"""

# 多个类应用 --- 房子和家具
"""class Jiaju(object):
    def __init__(self, name, area):
        self.name = name
        self.area = area

    def __str__(self):
        return f'家具类型：{self.name}，家具的占地面积是：{self.area}'

class Home(object):
    def __init__(self, address, area):
        self.address = address
        self.area = area
        self.free_area = area   # 剩余面积
        self.JiajuList = []   # 存放的家具列表

    def add_jiaju(self, item):
        if item.area < self.free_area:
            self.JiajuList.append(item.name)
            self.free_area -= item.area
        else:
            print('家具占地面积超过剩余面积！无法放置！')

    def __str__(self):
        return f'房子坐落于：{self.address}，占地面积是：{self.area}，剩余面积：{self.free_area}，家具列表：{self.JiajuList}'

bed = Jiaju('单人床', 200)
guizi = Jiaju('柜子', 100)
doublebed = Jiaju('双人床', 500)
desk = Jiaju('桌子', 150)

home = Home('深圳', 800)
home.add_jiaju(bed)
home.add_jiaju(doublebed)
home.add_jiaju(desk)
print(home)
"""

# 类的继承
# class A(object)     // 默认继承基类object
# class B(A)    // 单继承 B类继承A类，会继承A类中所有的属性和方法
# class B(A,C,D)    // 多继承 B类继承多个类，
# 1. 当多个类有同名的属性和方法时，默认继承第一个父类的同名属性和方法
# 2. 当子类中有与父类同名的属性和方法时， 优先使用的是子类的同名属性和方法
# 2.1 如果想要使用父类的同名属性和函数则需要加载父类的__init__(self)函数，并且需要传入self，否则self指的是子类自己的属性和方法
# 2.2 同时也需要在子类中调用自己的__init__函数，否则此时的self指的是上一次父类的self
# 可以通过 类.__mro__ 查看类继承的父类信息

class A(object):
    def __init__(self):
        self.name = '[我是 A 中的name]'

    def info(self):
        print(self.name)


class C(object):
    def __init__(self):
        self.name = '[我是 C 中的name]'

    def info(self):
        print(self.name)

# 单继承 继承A类
"""class B(A):   
    pass     # pass 占位符
b = B()
b.info()"""

# 多继承
# 继承的多个父类中含有相同的属性和方法，默认继承第一个父类
"""class B(C, A):   
    pass
b = B()
b.info()  
"""

# 继承的多个父类中含有与子类相同的属性和方法，默认使用自己的属性和方法
"""class B(C, A):
    def __init__(self):
        self.name = '[我是 B 中的name]'

    def info(self):
        print(self.name)
b = B()
b.info()  
"""

# 当子类和父类有相同属性和方法，都想使用时，需要写上init初始化方式
"""class B(C, A):
    def __init__(self):
        self.name = '[我是 B 中的name]'

    def info(self):
        self.__init__()    # 如果子类不写init则self指向上一个执行的父类
        print(self.name)

    def A_info(self):     # 如果父类不写init则self指向的是子类
        A.__init__(self)  # 在子类中调用父类的方法：父类.方法
        A.info(self)

    def C_info(self):
        C.__init__(self)
        C.info(self)
b = B()
b.info()
b.A_info()
b.C_info()
b.info()
print(B.__mro__)   # 查看B类继承了哪些父类
"""

# super函数可以直接访问到继承的父类中的方法
# 不带参数： super().父类方法   // 常用方法
# 带参数： super(当前类名， self).父类方法
"""class B(A):
    def __init__(self):
        self.name = '[我是 B 中的name]'

    def info(self):
        print(self.name)

    def A_info(self):
        # super访问父类的方法
        super().__init__()  # 不带参数
        super().info()

class D(B):
    def __init__(self):
        self.name = '[我是 D 中的name]'

    def info(self):
        self.__init__()    # 如果子类不写init则self指向上一个执行的父类
        print(self.name)

    def B_info(self):
        # super访问父类的两种方法
        super().__init__()   # 不带参数
        super().info()
        # super(D, self).__init__()  # 带参数super（当前类，self）.父类函数
        # super(D, self).info()
d = D()
d.info()
d.B_info()
d.A_info()"""

# 私有属性和方法写法： 在属性和方法前加两个下划线 __
"""class B(object):
    def __init__(self):
        self.name = 'caicai'
        self.__money = 20000   # 私有属性

    def info_name(self):
        print(self.name)

    def __info_money(self):
        print(self.__money)   # 私有方法

    # 提供访问/修改私有属性和方法的函数
    def get_money(self):    # 调用该函数获取私有属性
        return self.__money

    def set_money(self, m):
        self.__money = m     # 接收外部参数，修改私有属性
        return self.__money

class F(B):
    pass
f = F()
f.info_name()
print(f.get_money())   # 获取私有属性
f.set_money(20)   # 修改私有属性
print(f.get_money())
# f.__info_money()   # 私有方法无法访问到
"""

# 多态性
# 父类提供公共方法，子类重写父类方法,不同de对象实现不同的结果
"""class Dog(object):
    def work(self):
        print('追击敌人.....')
class Adog(Dog):
    def work(self):
        print('追击毒品.....')
class Bdog(Dog):
    def work(self):
        print('追击杀手.....')
class Cdog(Dog):
    def work(self):
        print('追击毒贩.....')
class person(object):
    def dog_work(self,dog):
        dog.work()
ad = Adog()
bd = Bdog()
cd = Cdog()

pd = person()
pd.dog_work(ad)
pd.dog_work(bd)
pd.dog_work(cd)
"""

# 类属性和类方法
class A(object):
    A_name = '我是A类'   # 类属性

    @classmethod    # 类方法
    def get_A_name(cls):   # cls指向该类A
        return cls.A_name + '123'

    @staticmethod   # 静态方法
    def info():   # 不需要传递cls，self
        print('我是静态方法')

print(A.A_name)     # 类对象调用
print(A.get_A_name())
A.info()
a = A()
print(a.A_name)
print(a.get_A_name())
a.info()
